Raspberry Piを極限まで無駄なくバックアップする
はじめに
Raspberry PiのSDカードの取り扱う上で、複製したり、イメージとして保存しておきたいシーンは多いと思います。 しかし、大容量のSDカードが一般的になった今では、空き容量が大半を占めるベタイメージを作成することは、以下の点から現実的でありません。
- イメージファイルの大半を空き容量が占め、時間およびストレージの効率が悪い
- 元より容量の小さいSDカードに書き込むことができない
- SDカードに書き込む際、空き領域にも書き込みが行われ、カードの寿命を縮めてしまう
- ファイルシステムが破損していても、コピー時に気づくことがない
パーティションの構造とLinuxの操作を学びながら、SDカードのバックアップをしていきましょう。
背景
Raspberry PiのSDカードの論理構造を見ていきましょう。難しく考える必要はありません。大体こんな感じといったイメージを掴むだけでOKです。
まず、セクタ単位でパーティションの一覧を表示してみます。 SDカードのデバイス名は、/dev/sdeとします。
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p Model: Generic- SD/MMC (scsi) Disk /dev/sde: 62586880s Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 8192s 96663s 88472s primary fat32 lba 2 98304s 30031871s 29933568s primary ext4
- 1つ目のパーティションが/bootで、Windows PCに挿したときにファイルが見える部分です。
- 2つ目のパーティションが/で、Linuxファイルシステム(ext4)が格納されています。Windows PCに挿すとフォーマットするか聞かれる部分です。
では、これを図にしてみましょう。
2つのパーティションのほかに、パーティションが割り当てられていない領域が二か所ありますね。 ここにはセクタ0のMBRのほか、機種依存のデータなどが格納されている場合がありますので、パーティション2以外の領域は念のため完全なコピーを行う方針としましょう。
おや、パーティション2の後に空き領域がありますね。このSDカード自体が別のイメージを複製して作成されたものだからです。今回の手順では、ファイルシステムの内容だけをコピーするため、問題ありません。
バックアッププランとして下記のことを行えばよいことになります。
- SDカード全体のうち、パーティション2の開始セクタの手前までをダンプする
- パーティション2の内容をバックアップする
やるべきことを書き出してみると、Linuxの基本的なツールで出来そうな気がしてきませんか?
- デバイスのセクタをダンプしたい…ddコマンド
- Linuxファイルシステム(ext2/3/4)をバックアップ/復元したい…dump/restoreコマンド
バックアップ
では早速やってみましょう。
パーティションテーブルを確認する
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p Model: Generic- SD/MMC (scsi) Disk /dev/sde: 62586880s Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 8192s 96663s 88472s primary fat32 lba 2 98304s 30031871s 29933568s primary ext4
パーティション2の開始セクタは98304sです。
※この値は最初に書き込んだイメージごとに違いますので、かならずバックアップするSDカード毎に確認してください
念のためパーティションテーブルをテキストに保存する
ddで保存したイメージ内のパーティションテーブルを見るのは大変そうなので、念のためです。
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p >pipart.txt
SDカード全体のうち、パーティション2の開始セクタの手前までをダンプする
セクタサイズは512バイトです。今回の場合は0~98303の98304セクタを保存したいので、
mc@u-west:~/share/temp$ sudo dd if=/dev/sde of=piboot.bin bs=512 count=98304 98304+0 レコード入力 98304+0 レコード出力 50331648 バイト (50 MB) コピーされました、 2.70878 秒、 18.6 MB/秒
パーティション2をdumpコマンドでバックアップする
せっかくなので、-z9 オプションを付けて圧縮し小さくしてみましょう。
mc@u-west:~/share/temp$ sudo dump -0 -z9 -f piroot.dump /dev/sde2 DUMP: Date of this level 0 dump: Thu Apr 2 11:14:49 2020 DUMP: Dumping /dev/sde2 (an unlisted file system) to piroot.dump DUMP: Label: rootfs DUMP: Writing 10 Kilobyte records DUMP: Compressing output at compression level 9 (zlib) DUMP: mapping (Pass I) [regular files] DUMP: mapping (Pass II) [directories] DUMP: estimated 3275017 blocks. DUMP: Volume 1 started with block 1 at: Thu Apr 2 11:15:02 2020 DUMP: dumping (Pass III) [directories] DUMP: dumping (Pass IV) [regular files] DUMP: 89.42% done at 9762 kB/s, finished in 0:00 DUMP: Closing piroot.dump DUMP: Volume 1 completed at: Thu Apr 2 11:20:33 2020 DUMP: Volume 1 took 0:05:31 DUMP: Volume 1 transfer rate: 4418 kB/s DUMP: Volume 1 3281440kB uncompressed, 1462543kB compressed, 2.244:1 DUMP: 3281440 blocks (3204.53MB) on 1 volume(s) DUMP: finished in 331 seconds, throughput 9913 kBytes/sec DUMP: Date of this level 0 dump: Thu Apr 2 11:14:49 2020 DUMP: Date this dump completed: Thu Apr 2 11:20:33 2020 DUMP: Average transfer rate: 4418 kB/s DUMP: Wrote 3281440kB uncompressed, 1462543kB compressed, 2.244:1 DUMP: DUMP IS DONE
※もし、この手順の途中にエラーが発生した場合は、ファイルシステムにエラーが残っている可能性がありますので、次の項を参照してください。
mc@u-west:~/share/temp$ ls -al 合計 1511744 drwxr-xr-x 2 mc mc 58 4月 2 13:09 . drwxrwxrwx 11 root root 45056 4月 2 11:22 .. -rw-r--r-- 1 root root 50331648 4月 2 11:05 piboot.bin -rw-r--r-- 1 mc mc 304 4月 2 11:04 pipart.txt -rw-r--r-- 1 root root 1497644568 4月 2 11:20 piroot.dump
バックアップデータは約1.5GBとなりました。
バックアップ時にエラーが起きたときは
見る
石橋をたたいて(-n オプション:書き込みを行わない)fsckします。
mc@u-west:~/share/temp$ sudo fsck -vnf /dev/sde2 fsck from util-linux 2.20.1 e2fsck 1.42.5 (29-Jul-2012) Pass 1: Checking inodes, blocks, and sizes Pass 2: Checking directory structure '..' directory entry in directory inode 171251 is not NULL terminated Fix? no Directory inode 171251, block #0, offset 80: directory corrupted Salvage? no e2fsck: aborted rootfs: ********** WARNING: Filesystem still has errors **********
fsckします(本番)。
mc@u-west:~/share/temp$ sudo fsck -vf /dev/sde2 fsck from util-linux 2.20.1 e2fsck 1.42.5 (29-Jul-2012) Pass 1: Checking inodes, blocks, and sizes Pass 2: Checking directory structure '..' directory entry in directory inode 171251 is not NULL terminated Fix? yes (中略) Pass 5: Checking group summary information rootfs: ***** FILE SYSTEM WAS MODIFIED ***** 105912 inodes used (11.38%, out of 931040) 69 non-contiguous files (0.1%) 111 non-contiguous directories (0.1%) # of inodes with ind/dind/tind blocks: 0/0/0 Extent depth histogram: 87618/18 872219 blocks used (23.31%, out of 3741696) 0 bad blocks 1 large file 78683 regular files 8668 directories 55 character device files 25 block device files 0 fifos 1375 links 18470 symbolic links (18186 fast symbolic links) 2 sockets ------------ 107278 files
これで、エラーなくdumpコマンドが実行できるはずです。
リストア
パーティションテーブルを確認する
間違ったデバイスに書き込んでしまうと取り返しがつかないので、かならず確認しましょう。
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p [sudo] password for mc: Model: Generic- SD/MMC (scsi) Disk /dev/sde: 62586880s Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 8192s 62586879s 62578688s primary fat32 lba
新品のSDHCカードはこのように、パーティションが一つだけ存在します。 SDXCカードの場合は、exfatのパーティションが存在しますが、いずれにせよ次の手順で上書きされてしまうため、気にしなくて問題ありません。
パーティション2の開始セクタの手前までを復元する
mc@u-west:~/share/temp$ sudo dd if=piboot.bin of=/dev/sde bs=65536 768+0 レコード入力 768+0 レコード出力 50331648 バイト (50 MB) コピーされました、 4.11515 秒、 12.2 MB/秒
パーティションテーブルを確認する
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p Model: Generic- SD/MMC (scsi) Disk /dev/sde: 62586880s Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 8192s 96663s 88472s primary fat32 lba 2 98304s 30031871s 29933568s primary
コピー元のカードのパーティションテーブルが上書きされるため、元のSDカードと同様のパーティションテーブルになります。 パーティション2はまだ中身がからっぽのため、File systemの欄が空欄になっています。
パーティション2を作り直す
mc@u-west:~/share/temp$ sudo parted /dev/sde GNU Parted 2.3 Using /dev/sde Welcome to GNU Parted! Type 'help' to view a list of commands. (parted) rm 2 (parted) mkpart primary ext4 98304s -1s (parted) q Information: You may need to update /etc/fstab.
rm 2 でパーティション2をいったん削除します。
mkpart primary ext4 98304s -1s で98304セクタからディスク末尾までのパーティションを作成します。
末尾を「-1s」と指定できることを知るまでは、全セクタ数から1引いて入力していました。こういう指定ができるのは便利ですね。
パーティションテーブルを確認する
mc@u-west:~/share/temp$ sudo parted /dev/sde u s p Model: Generic- SD/MMC (scsi) Disk /dev/sde: 62586880s Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 8192s 96663s 88472s primary fat32 lba 2 98304s 62586879s 62488576s primary
パーティション2はSDカードの末尾までぴったり収まっています。理想的ですね。
パーティション2をext4でフォーマットする
mc@u-west:~/share/temp$ sudo mkfs.ext4 /dev/sde2 mke2fs 1.42.5 (29-Jul-2012) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 1954064 inodes, 7811072 blocks 390553 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=4294967296 239 block groups 32768 blocks per group, 32768 fragments per group 8176 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done
これで、パーティション2がext4でフォーマットされました。次の手順で内容を復元していきましょう。
パーティション2をマウントし、restoreコマンドを実行
restoreコマンドの転送先はカレントディレクトリになるので、ファイルシステムをマウントし、マウントされたディレクトリにcdします。
mc@u-west:~/share/temp$ mkdir mount mc@u-west:~/share/temp$ sudo mount /dev/sde2 mount mc@u-west:~/share/temp$ cd mount
restoreコマンドを実行します。
mc@u-west:~/share/temp/mount$ sudo restore -rf ../piroot.dump Dump tape is compressed. restore: ./lost+found: File exists
これで、パーティション2の内容が復元されました。
アンマウントして取り外す
最後の手順です。ここで焦ると台無しになります。 大事なデータは、まだ書き込みバッファにあるかもしれません。
mc@u-west:~/share/temp/mount$ cd .. mc@u-west:~/share/temp$ sudo umount mount mc@u-west:~/share/temp$ sync mc@u-west:~/share/temp$ sync mc@u-west:~/share/temp$ sync
お疲れさまでした。これで、SDカードリーダを抜いてOKです。
応用
dumpコマンドとrestoreコマンドはパイプでつなぐことができます。(その際、圧縮は無効にしましょう!)SDカードからSDカードに直接コピーする際も、この方法なら無駄な書き込みが発生しないので、時間と寿命を節約できます。
パーティション2の開始セクタを認識して自動で処理するスクリプトにしてしまうのもよいでしょう。
起動中のRaspberry Pi自身のバックアップも取れます。(mount -o ro,remount / で一時的に読み取り専用でマウントする必要があるかもしれません。バックアップ先はUSBデバイスやネットワークなど、物理的に別のメディアが必要です。)
そのほかにも、Linuxを使う上での色々なアイデアを合わせることができますので、工夫して便利にしてみましょう。